package org.btik.p2p.ipv4;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.btik.context.thread.SystemControlAble;
import org.btik.light.tool.ByteUtil;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.concurrent.ConcurrentHashMap;

public class Ipv4P2pServer extends Thread implements SystemControlAble {
    private final Log log = LogFactory.getLog(Ipv4P2pServer.class);

    private final ConcurrentHashMap<String, P2pChannel> p2pChannelMap = new ConcurrentHashMap<>();

    private boolean runFlag = true;


    private int p2pServerPort;

    private DatagramSocket serverSocket;

    /**
     * 通道刷新时间，当长时间间隔，没有对该通道请求，下一次请求该通道，会导致该通道重置
     */
    private int channelAliveTime = 30 * 1000;

    public void setEnable(boolean enable) {
        // 仅供start之前调用
        if (isAlive()) {
            return;
        }
        this.runFlag = enable;
    }

    public void setChannelAliveTime(int channelAliveTime) {
        this.channelAliveTime = channelAliveTime;
    }

    public void setP2pServerPort(int p2pServerPort) {
        this.p2pServerPort = p2pServerPort;
    }

    @Override
    public synchronized void start() {
        if(!runFlag){
            log.info("p2p server is disabled");
            return;
        }
        if (p2pServerPort == 0) {
            throw new IllegalArgumentException("p2p Server Port not specified");
        }
        super.start();
        log.info("p2p server started");
    }

    @Override
    public void run() {
        try (DatagramSocket serverSocket = new DatagramSocket(p2pServerPort)) {
            this.serverSocket = serverSocket;
            byte[] buffer = new byte[1024];
            DatagramPacket datagramPacket = new DatagramPacket(buffer, 0, buffer.length);
            while (runFlag) {
                serverSocket.receive(datagramPacket);
                int length = datagramPacket.getLength();

                if (length < 2) {
                    continue;
                }

                byte[] data = datagramPacket.getData();
                String channelId = new String(data, 1, data.length - 1);
                // buffer无需再引用，下面部分可以异步
                P2pChannel p2pChannel = p2pChannelMap
                        .computeIfAbsent(channelId, (key) -> new P2pChannel(channelId));
                if (p2pChannel.getLastActiveTime() - System.currentTimeMillis() > channelAliveTime) {
                    p2pChannel.reset();
                }
                p2pChannel.setLastActiveTime(System.currentTimeMillis());
                InetAddress address = datagramPacket.getAddress();
                int port = datagramPacket.getPort();
                byte[] remoteAddress = address.getAddress();

                switch (buffer[0]) {
                    case 'S' -> {
                        p2pChannel.setSender(remoteAddress);
                        p2pChannel.setSenderPort(port);
                        byte[] receiver = p2pChannel.getReceiver();
                        if (receiver == null) {
                            break;
                        }
                        int receiverPort = p2pChannel.getReceiverPort();
                        sendMsg(ByteUtil.ipv4AndPort2Bytes(receiver, receiverPort), remoteAddress, port);

                    }
                    case 'R' -> {
                        p2pChannel.setReceiver(remoteAddress);
                        p2pChannel.setReceiverPort(port);
                        byte[] sender = p2pChannel.getSender();
                        if (sender == null) {
                            break;
                        }
                        int senderPort = p2pChannel.getSenderPort();
                        sendMsg(ByteUtil.ipv4AndPort2Bytes(sender, senderPort), remoteAddress, port);
                    }
                    default -> {
                    }
                }

            }
        } catch (IOException e) {
            log.info(" start server failed:" + e.getMessage());
        }

        log.info("p2p is down");
    }

    void sendMsg(byte[] msg, byte[] targetIp, int port) throws IOException {
        DatagramPacket packet = new DatagramPacket(msg, 0, msg.length);
        packet.setPort(port);
        packet.setAddress(InetAddress.getByAddress(targetIp));
        serverSocket.send(packet);
    }

    @Override
    public void shutdown(String msg) {
        if (!isAlive()) {
            return;
        }
        runFlag = false;

    }


}
